//The implementation of class _3D_Vector
//Author: NianchenDeng (5080379114)
#include "_3D_Vector.h"

// ========================================================================
// Constructors & Destructor
// ========================================================================

// Construct an zero vector
_3D_Vector::_3D_Vector ():_Length (0)
{
  _Arr = new VType[3];
  for (uint i = 0; i < 3; ++i)
    _Arr[i] = 0.0;
}

// Construct a vector point from vertex1 to vertex2
_3D_Vector::_3D_Vector (const VType *vertex1, const VType *vertex2)
{
  _Arr = new VType[3];
  assign_value (vertex1, vertex2);
}

// Construct a vector by an array
_3D_Vector::_3D_Vector (const VType *source)
{
  _Arr = new VType[3];
  assign_value (source);
}

// Construct a vector by coordinate
_3D_Vector::_3D_Vector (VType x, VType y, VType z)
{
  _Arr = new VType[3];
  _Arr[0] = x; _Arr[1] = y; _Arr[2] = z;
}

// Copy constructor
_3D_Vector::_3D_Vector (const _3D_Vector& source)
{
  _Arr = new VType[3];
  assign_value (source.data ());
}

_3D_Vector::~_3D_Vector ()
{
  delete []_Arr;
}

// ========================================================================
// Self-operation functions
// ========================================================================

_3D_Vector& _3D_Vector::operator= (const _3D_Vector& right)
{
  assign_value (right.data ());
  return (*this);
}

_3D_Vector& _3D_Vector::operator+= (const _3D_Vector& right)
{
  for (uint i = 0; i < 3; ++i)
    _Arr[i] += right[i];
  return (*this);
}


_3D_Vector& _3D_Vector::operator-= (const _3D_Vector& right)
{
  for (uint i = 0; i < 3; ++i)
    _Arr[i] -= right[i];
  return (*this);
}


_3D_Vector& _3D_Vector::operator*= (const VType& right)
{
  for (int i = 0; i < 3; ++i)
    _Arr[i] *= right;
  return (*this);
}

_3D_Vector& _3D_Vector::operator/= (const VType& right)
{
  for (int i = 0; i < 3; ++i)
    _Arr[i] /= right;
  return (*this);
}

_3D_Vector& _3D_Vector::unitary ()
{
  calc_length ();
  if (_Length == 0) return *this;
  for (uint i = 0; i < 3; ++i)
  {
    _Arr[i] /= _Length;
  }
  return (*this);
}

// ========================================================================
// Get functions
// ========================================================================

double _3D_Vector::length () const
{
  calc_length ();
  return _Length;
}

VType& _3D_Vector::operator[] (uint pos) const
{
  return _Arr[pos];
}

const VType* _3D_Vector::data () const
{
  return _Arr;
}

// ========================================================================
// Operation functions
// ========================================================================

_3D_Vector _3D_Vector::operator+ (const _3D_Vector &right) const
{
  _3D_Vector answer;
  for (uint i = 0; i < 3; ++i)
    answer[i] = _Arr[i] + right[i];
  return answer;
}


_3D_Vector _3D_Vector::operator- (const _3D_Vector &right) const
{
  _3D_Vector answer;
  for (uint i = 0; i < 3; ++i)
    answer[i] = _Arr[i] - right[i];
  return answer;
}

//Here are two forms of number-multiply operator
_3D_Vector _3D_Vector::operator* (const VType& right) const
{
  _3D_Vector answer;
  for (uint i = 0; i < 3; ++i)
    answer[i] = _Arr[i] * right;
  return answer;
}

_3D_Vector _3D_Vector::operator/ (const VType& right) const
{
  if (right == 0)
    throw ("Divided by zero.");
  _3D_Vector answer;
  for (uint i = 0; i < 3; ++i)
    answer[i] = _Arr[i] / right;
  return answer;
}

//This is dot-multiply operator
double _3D_Vector::dot (const _3D_Vector& right) const
{
  double answer = 0;
  for (uint i = 0; i < 3; ++i)
    answer += _Arr[i] * right[i];
  return answer;
}

_3D_Vector _3D_Vector::cross (const _3D_Vector& right) const
{
  _3D_Vector answer;
  answer[0] = _Arr[1] * right[2] - _Arr[2] * right[1];
  answer[1] = _Arr[2] * right[0] - _Arr[0] * right[2];
  answer[2] = _Arr[0] * right[1] - _Arr[1] * right[0];
  return answer;
}

_3D_Vector _3D_Vector::operator- () const
{
  _3D_Vector ans = *this;
  ans._Arr[0] = -ans._Arr[0];
  ans._Arr[1] = -ans._Arr[1];
  ans._Arr[2] = -ans._Arr[2];
  return ans;
}

// ========================================================================
// Relation functions
// ========================================================================

//The return value is in radian, not degree
double _3D_Vector::angle (const _3D_Vector& right) const
{
  if (length () == 0 || right.length () == 0)
    return 0;
  return acos (this->dot (right) / (length () * right.length ()));
}

double _3D_Vector::distance (const _3D_Vector& right) const
{
  long double sum = 0.0;
  for (uint i = 0; i < 3; ++i)
    sum += (_Arr[i] - right[i]) * (_Arr[i] - right[i]);
  return sqrt (sum);
}

// ========================================================================
// Private functions
// ========================================================================

//Calculate the length of vector
void _3D_Vector::calc_length () const
{
  long double sum = 0.0;
  for (uint i = 0; i < 3; ++i)
    sum += _Arr[i] * _Arr[i];
  _Length = sqrt (sum);
}

//Get value from an array
void _3D_Vector::assign_value (const VType* coordinate)
{
  for (uint i = 0; i < 3; ++i)
  {
    _Arr[i] = coordinate[i];
  }
}

//Get value from two vertexes
//to set the vector pointing from vertex1 to vertex2
void _3D_Vector::assign_value (const VType* vertex1, const VType* vertex2)
{
  for (uint i = 0; i < 3; ++i)
  {
    _Arr[i] = vertex2[i] - vertex1[i];
  }
}